import {
  BufferGeometry,
  Geometry,
  Line,
  Matrix3,
  Mesh,
  Vector2,
  Vector3
} from "../../build/three.module.js";

var OBJExporter = function () {
};

OBJExporter.prototype = {

  constructor: OBJExporter,

  parse: function (object) {

    var output = '';

    var indexVertex = 0;
    var indexVertexUvs = 0;
    var indexNormals = 0;

    var vertex = new Vector3();
    var normal = new Vector3();
    var uv = new Vector2();

    var i, j, k, l, m, face = [];

    var parseMesh = function (mesh) {

      var nbVertex = 0;
      var nbNormals = 0;
      var nbVertexUvs = 0;

      var geometry = mesh.geometry;

      var normalMatrixWorld = new Matrix3();

      if (geometry instanceof Geometry) {

        geometry = new BufferGeometry().setFromObject(mesh);

      }

      if (geometry instanceof BufferGeometry) {

        // shortcuts
        var vertices = geometry.getAttribute('position');
        var normals = geometry.getAttribute('normal');
        var uvs = geometry.getAttribute('uv');
        var indices = geometry.getIndex();

        // name of the mesh object
        output += 'o ' + mesh.name + '\n';

        // name of the mesh material
        if (mesh.material && mesh.material.name) {

          output += 'usemtl ' + mesh.material.name + '\n';

        }

        // vertices

        if (vertices !== undefined) {

          for (i = 0, l = vertices.count; i < l; i++, nbVertex++) {

            vertex.x = vertices.getX(i);
            vertex.y = vertices.getY(i);
            vertex.z = vertices.getZ(i);

            // transform the vertex to world space
            vertex.applyMatrix4(mesh.matrixWorld);

            // transform the vertex to export format
            output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n';

          }

        }

        // uvs

        if (uvs !== undefined) {

          for (i = 0, l = uvs.count; i < l; i++, nbVertexUvs++) {

            uv.x = uvs.getX(i);
            uv.y = uvs.getY(i);

            // transform the uv to export format
            output += 'vt ' + uv.x + ' ' + uv.y + '\n';

          }

        }

        // normals

        if (normals !== undefined) {

          normalMatrixWorld.getNormalMatrix(mesh.matrixWorld);

          for (i = 0, l = normals.count; i < l; i++, nbNormals++) {

            normal.x = normals.getX(i);
            normal.y = normals.getY(i);
            normal.z = normals.getZ(i);

            // transform the normal to world space
            normal.applyMatrix3(normalMatrixWorld).normalize();

            // transform the normal to export format
            output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';

          }

        }

        // faces

        if (indices !== null) {

          for (i = 0, l = indices.count; i < l; i += 3) {

            for (m = 0; m < 3; m++) {

              j = indices.getX(i + m) + 1;

              face[m] = (indexVertex + j) + (normals || uvs ? '/' + (uvs ? (indexVertexUvs + j) : '') + (normals ? '/' + (indexNormals + j) : '') : '');

            }

            // transform the face to export format
            output += 'f ' + face.join(' ') + "\n";

          }

        } else {

          for (i = 0, l = vertices.count; i < l; i += 3) {

            for (m = 0; m < 3; m++) {

              j = i + m + 1;

              face[m] = (indexVertex + j) + (normals || uvs ? '/' + (uvs ? (indexVertexUvs + j) : '') + (normals ? '/' + (indexNormals + j) : '') : '');

            }

            // transform the face to export format
            output += 'f ' + face.join(' ') + "\n";

          }

        }

      } else {

        console.warn('THREE.OBJExporter.parseMesh(): geometry type unsupported', geometry);

      }

      // update index
      indexVertex += nbVertex;
      indexVertexUvs += nbVertexUvs;
      indexNormals += nbNormals;

    };

    var parseLine = function (line) {

      var nbVertex = 0;

      var geometry = line.geometry;
      var type = line.type;

      if (geometry instanceof Geometry) {

        geometry = new BufferGeometry().setFromObject(line);

      }

      if (geometry instanceof BufferGeometry) {

        // shortcuts
        var vertices = geometry.getAttribute('position');

        // name of the line object
        output += 'o ' + line.name + '\n';

        if (vertices !== undefined) {

          for (i = 0, l = vertices.count; i < l; i++, nbVertex++) {

            vertex.x = vertices.getX(i);
            vertex.y = vertices.getY(i);
            vertex.z = vertices.getZ(i);

            // transform the vertex to world space
            vertex.applyMatrix4(line.matrixWorld);

            // transform the vertex to export format
            output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n';

          }

        }

        if (type === 'Line') {

          output += 'l ';

          for (j = 1, l = vertices.count; j <= l; j++) {

            output += (indexVertex + j) + ' ';

          }

          output += '\n';

        }

        if (type === 'LineSegments') {

          for (j = 1, k = j + 1, l = vertices.count; j < l; j += 2, k = j + 1) {

            output += 'l ' + (indexVertex + j) + ' ' + (indexVertex + k) + '\n';

          }

        }

      } else {

        console.warn('THREE.OBJExporter.parseLine(): geometry type unsupported', geometry);

      }

      // update index
      indexVertex += nbVertex;

    };

    object.traverse(function (child) {

      if (child instanceof Mesh) {

        parseMesh(child);

      }

      if (child instanceof Line) {

        parseLine(child);

      }

    });

    return output;

  }

};

export {OBJExporter};
